//
// Created on 2025/1/20.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".
#include "Testing.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTraceEventCommon.h"
#include "utils/log.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include <native_drawing/drawing_rect.h>
#include <string>
#include <map>


void TestRegistry::registerTestCase(const char *name, Test_Canvas_Fun fun, TEST_LEVEL level, int height) {
    if (level == TEST_LEVEL::NORMAL) {
        fAllNormalTestingCases.push_back({height, fun, nullptr, nullptr, 0, std::string(name)});
    } else if (level == TEST_LEVEL::SELF_TESTING) {
        fAllSelfTestingCases.push_back({height, fun, nullptr, nullptr, 0, std::string(name)});
        
    }
}

std::list<Test_Case> &TestRegistry::getTestingCaseList() {
    if (fAllSelfTestingCases.size() != 0) {
        return fAllSelfTestingCases;
    } else {
        return fAllNormalTestingCases;
    }
}

void Testing::setDrawCanvas(OH_Drawing_Canvas *canvas, int width, int height) {
    if (fCanvas) {
        fCanvas = new SkCanvas(canvas);
        fOHCanvas = canvas;
    } else {
        if (fOHCanvas != canvas) {
            delete fCanvas;
            fCanvas = new SkCanvas(canvas);
        }
    }
    fWindowWidth = width;
    fWindowHeight = height;
}

void Testing::touchPointMove(int touchType, int x, int y, int id) {
    if (id >= 10) {
        return;
    }
    LOGI("id %{public}d touch %{public}d x %{public}d y %{public}d  ", id, touchType, x, y);
    
    bool hasMove = false;
    fTouchPosition[id].x = x;
    fTouchPosition[id].y = y;
    if (touchType == 0) {
        // down
        fTouchFlag[id] = true;
    } else if (touchType == 1) {
        // move
        hasMove = true;
    } else {
        // up / cancel
        fTouchFlag[id] = false;
    }

    int centerX, centerY;
    int sumX = 0, sumY = 0, touchCnt = 0;
    int index1 = -1, index2 = -1;
    for (int i = 0; i < 10; i++) {
        if (fTouchFlag[i]) {
            sumX += fTouchPosition[i].x;
            sumY += fTouchPosition[i].y;
            touchCnt++;
            if (index1 == -1) {
                index1 = i;
            } else if (index2 == -1) {
                index2 = i;
            }
        }
    }
    if (touchCnt == 0) {
        return;
    }
    centerX = sumX / touchCnt;
    centerY = sumY / touchCnt;
    if (hasMove) {
        int dx = centerX - fLastCenterX;
        int dy = centerY - fLastCenterY;
        fCanvasMatrix.postTranslate(dx, dy);
    }
    fLastCenterX = centerX;
    fLastCenterY = centerY;
    
    // scale
    float dis = 0.f;
    if (touchCnt == 2) {
        dis = sqrt((fTouchPosition[index1].x - fTouchPosition[index2].x) *
                       (fTouchPosition[index1].x - fTouchPosition[index2].x) +
                   (fTouchPosition[index1].y - fTouchPosition[index2].y) *
                       (fTouchPosition[index1].y - fTouchPosition[index2].y));
        if (hasMove && fLastDistance > 0.f) {
            float scale = (dis - fLastDistance) / fLastDistance + 1;
            fCanvasMatrix.postTranslate(-fLastCenterX, -fLastCenterY);
            fCanvasMatrix.postScale(scale, scale);
            fCanvasMatrix.postTranslate(fLastCenterX, fLastCenterY);
        }
        fLastDistance = dis;
    } else {
        fLastDistance = 0;
    }
}

void Testing::startTesting(SkCanvas *canvas) {
    if (canvas != nullptr) {
        fCanvas = canvas;
//        fCanvas->disableSubCanvas();
    }
    runTestCases(TestRegistry::GetInstance().getTestingCaseList()); 
}

void Testing::addPixelmapData(OH_PixelmapNative *pixelmap) {
    pixelmapDatas.push_back(pixelmap);
}

int Testing::getPixelmapNum() {
    return pixelmapDatas.size();
}

OH_PixelmapNative *Testing::getPixelmapData(int index) {
    return pixelmapDatas[index];
}

void Testing::addExternalNode(std::shared_ptr<OHRenderNode> node) {
    externalNodes.push_back(node);
}

int Testing::getNodeNum() {
    return externalNodes.size();
}

std::shared_ptr<OHRenderNode> Testing::getExternalNode(int index) {
    return externalNodes[index];    
}

void Testing::destroyExternalNode() {
    externalNodes.clear();
}

void Testing::addGifData(void *data, size_t length) {
    char *copy_data = new char[length];
    memcpy(copy_data, data, length);
    gifDatas.push_back({copy_data, length});
}

int Testing::getGifNum() { return gifDatas.size(); }

void *Testing::getGifData(size_t *length, int index) {
    if (index >= gifDatas.size()) {
        return nullptr;
    }
    auto &[real_data, real_length] = gifDatas[index];
    *length = real_length;
    return real_data;
}

void Testing::addPicData(void *data, size_t length) {
    char *copy_data = new char[length];
    memcpy(copy_data, data, length);
    picDatas.push_back({copy_data, length});
}

int Testing::getPicNum() { return picDatas.size(); }

void *Testing::getPicData(size_t *length, int index) {
    if (index >= picDatas.size()) {
        return nullptr;
    }
    auto &[real_data, real_length] = picDatas[index];
    *length = real_length;
    return real_data;
}

void Testing::buildTestCasePicture(Test_Case& test) {
    if (test.caseName == "drawGifImageRect") {
        return;
    }
    if (!test.caseRecorder) {
        test.caseRecorder = new SkPictureRecorder();
    }
    if (!test.casePicture) {
        auto record_canvas = test.caseRecorder->beginRecording(1000, 1000);
//        record_canvas->clipRect(SkRect::MakeXYWH(0, 0, 3000, test.caseHeight));
        test.caseFun(*record_canvas);
        test.casePicture = test.caseRecorder->finishRecordingAsPicture();
        test.picNum = Testing::GetInstance().getPicNum();
    }
}

void Testing::runTestCases(std::list<Test_Case> &test_cases) {
    int drawY = 0;
    int drawX = 0;
    
    auto windowCull = fCanvas->getDeviceClipBounds();
    fWindowWidth = windowCull.width();
    fWindowHeight = windowCull.height();
    LOGI("windows size: %{public}d  %{public}d ", fWindowWidth, fWindowHeight);

    fCanvas->setMatrix(fCanvasMatrix);
//    fCanvas->clear(SK_ColorRED);
//    fCanvas->scale(0.1, 0.1);
    SkRect windowArea = SkRect::Make(windowCull);
    if (fCanvasMatrix.getMinScale() < 0.05) {
        // for test
        fCanvas->forceDrawInPicture(true);
    }
    for (auto &test_case : test_cases) {
        std::string name = test_case.caseName;
        // LOGI("testing case: %{public}s", name.c_str());
        std::string trace_str = "Testing::" + name;
        fCanvas->save();
        fCanvas->translate(drawX, drawY);

        fCanvas->clipRect(SkRect::MakeXYWH(0, 0, 3000, test_case.caseHeight));

        auto nowCaseRect = SkRect::MakeXYWH(drawX, drawY, 3000, test_case.caseHeight);
        SkRect destRect;
        fCanvasMatrix.mapRect(&destRect, nowCaseRect);
        if (destRect.intersect(windowArea)) {
            TRACE_EVENT0("skia", trace_str.c_str());
            LOGI("drawing testing case: %{public}s", name.c_str());
//            test_case.caseFun(*fCanvas);
            buildTestCasePicture(test_case);
            if (test_case.casePicture) {
                fCanvas->drawPicture(test_case.casePicture);
            } else {
                test_case.caseFun(*fCanvas);
            }
        } else {
            LOGI("skip testing case: %{public}s", name.c_str());
            test_case.casePicture.reset();
        }
        fCanvas->restore();
        drawY += test_case.caseHeight;
    }
    
    drawY = 0;
    drawX = 3100;
//    fCanvas->disableSubCanvas(true);
//    fCanvas->forceDrawInPicture(true);
//    for (auto &test_case : test_cases) {
//        std::string name = test_case.caseName;
//        // LOGI("testing case: %{public}s", name.c_str());
//        std::string trace_str = "Testing::" + name;
//        fCanvas->save();
//        fCanvas->translate(drawX, drawY);
//
//        fCanvas->clipRect(SkRect::MakeXYWH(0, 0, 3000, test_case.caseHeight));
//
//        auto nowCaseRect = SkRect::MakeXYWH(drawX, drawY, 3000, test_case.caseHeight);
//        SkRect destRect;
//        fCanvasMatrix.mapRect(&destRect, nowCaseRect);
//        if (destRect.intersect(windowArea)) {
//            TRACE_EVENT0("skia", trace_str.c_str());
//            test_case.caseFun(*fCanvas);
//        }
//        fCanvas->restore();
//        drawY += test_case.caseHeight;
//    }
//    fCanvas->disableSubCanvas(false);
    
}


int gTesingCnt = 0;

void Testing::destroyAllTestCasePictures() {
    for (auto &test_case : TestRegistry::GetInstance().getTestingCaseList()) {
        test_case.casePicture.reset();
        delete test_case.caseRecorder;
        test_case.caseRecorder = nullptr;
    }
    gTesingCnt++;
}
